package com.songtech.base.universal.dao.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.songtech.base.universal.dao.beans.BeanParam;

/**
 * 获取实体对象的属性、值和表名
 * @param <T>
 */
public class GetEntityParamUtil {
	/**
	 * 获取实体对象对应的表名
	 * <br>若没有使用@Table注解表名，则使用类名作为表名(表名中间存在大写字符，则大写字符前加“_”)
	 * @param cls
	 * @return
	 */
	public String getTableName(Class<?> cls){
		if(cls.isAnnotationPresent(Table.class)){
			String tName = cls.getAnnotation(Table.class).name();
			// 没有设定表名则使用实体名称
			if("".equals(tName)){
				tName = this.getBeanName(cls);
			}
			return tName;
		}else{
			return this.getBeanName(cls);
		}
	}
	
	/**
	 * 获取当前传入的实体名称
	 * <br>若没有使用@Table注解表名，则使用类名作为表名(表名中间存在大写字符，则大写字符前加“_”)
	 * @param cls
	 * @return
	 */
	private String getBeanName(Class<?> cls){
		String tName = this.toTableString(cls.getSimpleName());
		String poName = tName.substring(tName.length() - 2, tName.length());
		if("object".equals(poName)){
			tName = tName.substring(0, tName.length() - 3);
		}
		return tName;
	}
	
	/**
	 * 传入字符串，如果字符串中间出现大写字符，则在大写字符前加“_”
	 * @param text
	 * @return
	 */
	public String toTableString(String text){
		String tName = text.substring(0, 1).toLowerCase();
		for(int i = 1; i < text.length(); i++){
			if(!Character.isLowerCase(text.charAt(i))){
				tName += "_";
			}
			tName += text.substring(i, i + 1);
		}
		return tName.toLowerCase();
	}
	
	/**
	 * 获取传入对象的属性和值
	 * @param model
	 * @param cls
	 * @return
	 */
	public List<BeanParam> getParam(Object model, Class<?> cls) {
		List<BeanParam> list = new ArrayList<>();
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				list.add(this.setBeanParam(field, field.get(model)));
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	/**
	 * 获取传入class的属性
	 * @param cls
	 * @return
	 */
	public List<BeanParam> getParam(Class<?> cls) {
		List<BeanParam> list = new ArrayList<>();
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				list.add(this.setBeanParam(field, null));
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	/**
	 * 获取传入实体类与表对应的字段和值(获取值不为null的属性)
	 * <br>通过定义的注解判断是否为数据库字段
	 * <br>若使用了@Column注解没有name值，则取该属性的名称
	 * @param model
	 * @param cls
	 * @return
	 */
	public List<BeanParam> getParamByBeanNotEmpty(Object model, Class<?> cls) {
		List<BeanParam> list = new ArrayList<>();
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				Object obj = field.get(model);
				if(obj != null){
					// 该属性未忽略
		    		if(!field.isAnnotationPresent(Transient.class)){
						list.add(this.setBeanParam(field, obj));
		    		}
				}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	/**
	 * 获取传入实体类与表对应的字段和值
	 * <br>通过定义的注解判断是否为数据库字段
	 * <br>若使用了@Column注解没有name值，则取该属性的名称
	 * @param model
	 * @param cls
	 * @return
	 */
	public List<BeanParam> getParamByBean(Object model, Class<?> cls) {
		List<BeanParam> list = new ArrayList<>();
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				// 该属性未忽略
	    		if(!field.isAnnotationPresent(Transient.class)){
					list.add(this.setBeanParam(field, field.get(model)));
	    		}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	/**
	 * 获取传入class实体与表对应的字段
	 * <br>通过定义的注解判断是否为数据库字段
	 * <br>若使用了@Column注解没有name值，则取该属性的名称
	 * @param cls
	 * @return
	 */
	public List<BeanParam> getParamByBean(Class<?> cls) {
		List<BeanParam> list = new ArrayList<>();
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				// 该属性未忽略
	    		if(!field.isAnnotationPresent(Transient.class)){
	    			list.add(this.setBeanParam(field, null));
	    		}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	/**
	 * 获取传入实体的id字段(主键)
	 * <br>通过@Id注解判断查找主键
	 * <br>若使用了@Column注解没有name值，则取该属性的名称
	 * @param model
	 * @param cls
	 * @return
	 */
	public BeanParam getParamById(Object model, Class<?> cls) {
		BeanParam p = null;
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				// 该属性是主键
	    		if(field.isAnnotationPresent(Id.class)){
					p = this.setBeanParam(field, field.get(model));
					break;
	    		}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return p == null ? new BeanParam() : p;
	}
	
	/**
	 * 获取传入class实体的id字段(主键(包括字段名、属性名、属性类型))
	 * <br>通过@Id注解判断查找主键
	 * <br>若使用了@Column注解没有name值，则取该属性的名称
	 * @param cls
	 * @return
	 */
	public BeanParam getParamById(Class<?> cls) {
		BeanParam p = null;
		try {
			for (Field field : cls.getDeclaredFields()) {
				// 不使用Java语言访问检查
				field.setAccessible(true);
				// 找到注解的id字段
	    		if(field.isAnnotationPresent(Id.class)){
					p = this.setBeanParam(field, null);
					break;
	    		}
			}
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return p == null ? new BeanParam() : p;
	}
	
	/**
	 * 传入一组BeanParam，返回BeanParam中的主键
	 * @param list
	 * @return
	 */
	public BeanParam getParamById(List<BeanParam> list) {
		for(BeanParam p : list){
			if(p.getJudgeKey()){
				return p;
			}
		}
		return new BeanParam();
	}
	
	/**
	 * 根据字段信息设置一个BeanParam对象
	 * @param field 字段
	 * @param value 字段中的值
	 * @return
	 */
	private BeanParam setBeanParam(Field field, Object value){
		String attrName = field.getName();
		String idField;
		boolean judgeKey = false;
		// 设置了字段对应名称则取属性名称
		if(field.isAnnotationPresent(Column.class)){
			idField = field.getAnnotation(Column.class).name();
			// 没有指定名称则使用属性名
			if("".equals(idField)){
				idField = attrName;
			}
			// 该属性是主键
    		if(field.isAnnotationPresent(Id.class)){
    			judgeKey = true;
    		}
		}else{
			idField = attrName;
		}
		return new BeanParam(idField, attrName, field.getType().getName(), value, judgeKey);
	}
	
	public static void main(String[] args) {
		GetEntityParamUtil gep = new GetEntityParamUtil();
		BeanParam param = new BeanParam();
		param.setFieldName("testname");
		param.setValue("test");
		List<BeanParam> list = gep.getParam(param, BeanParam.class);
		for(BeanParam p : list){
			System.out.println(p.getFieldName());
			System.out.println(p.getValue());
		}
//		System.out.println();
//		list = gep.getParam(BeanParam.class);
//		for(BeanParam p : list){
//			System.out.println(p.getFieldName());
//			System.out.println(p.getValue());
//		}
//		System.out.println();
//		list = gep.getParamByBean(param);
//		for(BeanParam p : list){
//			System.out.println(p.getFieldName());
//			System.out.println(p.getValue());
//		}
//		System.out.println();
//		list = gep.getParamByBean(BeanParam.class);
//		for(BeanParam p : list){
//			System.out.println(p.getFieldName());
//			System.out.println(p.getValue());
//		}
//		System.out.println();
//		System.out.println(gep.getTableName(BeanParam.class));
	}
}
